package recipe.renderer;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.Locale;
import java.util.Map;
import android.app.Activity;
import android.speech.tts.TextToSpeech;
import android.util.Log;
import android.widget.Toast;

public class Behaviour implements TextToSpeech.OnInitListener {
	private static final String TAG = "RecipeRender.Behaviour";

	public long query_delay = 20000;
	public long promotion_delay = 5000;
	public Recipe recipe;
	public boolean is_running;
	public long last_announcment_time;
	public Task last_queried_task;
	public long time_marker;
	public long time_started_recipe;
	private Recipe_rendererActivity parent;
	private Map<String, Long> eta_dict;
	private TextToSpeech tts;

	public Behaviour(Recipe_rendererActivity parent, Recipe rec, Activity context) {
		this.recipe = rec;
		this.parent = parent;
		is_running = false;
		time_started_recipe = 0;
		last_announcment_time = 0;
		last_queried_task = null;
		eta_dict = null;
		time_marker = System.currentTimeMillis();
		tts = new TextToSpeech(context,this);
	}
	
    // implementation of TextToSpeech.OnInitListener
    public void onInit(int status)
    {
    	if (status != TextToSpeech.SUCCESS){
    		Toast.makeText(null, "Could not init the TTS engine", Toast.LENGTH_LONG);
    		Log.e(TAG, "Could not initialize TextToSpeech.");
    		return;
    	}
    	
    	int result = tts.setLanguage(Locale.US);
    	
    	if (result == TextToSpeech.LANG_MISSING_DATA ||
                result == TextToSpeech.LANG_NOT_SUPPORTED) {
               // Language data is missing or the language is not supported.
                Log.e(TAG, "Language is not available.");
        }   	
    }	

	public void say(final String str) {
		parent.say(str);
		tts.speak(str,TextToSpeech.QUEUE_FLUSH,null);
	}

	private void set_last_queried_task() {
		Task lt = null;
		for (Task t : recipe.find_active_tasks())
			if ((lt == null) || (t.time_started > lt.time_started))
				lt = t;
		last_queried_task = lt;
	}
	
	public Map<String, Long> get_eta_dict(long time_marker) {
		if (eta_dict == null) {
			eta_dict = calculate_eta_dict(time_marker);
		}
		return eta_dict;
	}
	
	private Map<String, Long> calculate_eta_dict(long time_marker) {
	    /* simulate playing the recipe and returns the eta of the upcoming tasks
	    returns: dictionary of task_id to eta
	    */
		
		Recipe recipe_clone = new Recipe(recipe);
	    
	    // copy behavior state variables
	    long _last_announcment_time = last_announcment_time;
	    
	    // this is based on the player logic in idle()
	    // TODO:  DRY (don't repeat yourself)
	    //        somehow combine this simulated recipe player logic with the real player logic
	    long current_time = time_marker - 1000;
	    while (true) {
	    	LinkedList<Task> available_tasks = recipe_clone.find_available_tasks();
	    	LinkedList<Task> active_tasks = recipe_clone.find_active_tasks();
	    	
	        if (active_tasks.size()==0 && available_tasks.size()==0)
	            break;
	        
	        long min_finish_time = Long.MAX_VALUE;
	        Task next_task = null;
	        // find first task to become overdue
	        for (Task t : active_tasks) {
	        	long finish_time = t.time_started + t.duration*1000;
	        	if (finish_time < min_finish_time) {
	        		min_finish_time = finish_time;
	        		next_task = t;
	        	}
	        }
	        
	        
	        if (next_task != null) {
	        	current_time = min_finish_time;
	        	next_task.stop(current_time);
	        }
	        
	        
	        // we are gonna announce stuff. Don't do so too freq.
	        if (_last_announcment_time!=0 && (current_time-_last_announcment_time < promotion_delay))
	        	current_time = _last_announcment_time + promotion_delay;

	        // promote available tasks
	        
	        if (available_tasks.size() > 0) {
	            Task t = available_tasks.getFirst();
	            t.start(current_time);
	            _last_announcment_time = current_time;
	            continue;
	        }
	    
	        // no need to query for overdue tasks - we automatically stop them above
	    }
	    HashMap<String, Long> eta_dict = new HashMap<String, Long>();
	    for (Task t :  recipe_clone.tasks) {
	    	eta_dict.put(t.id, t.ETA());
	    }
	    return eta_dict;
	}

	public void idle() {
		say("idle func starting...\n");
		if ((recipe==null)||(recipe.tasks.size()==0)) {
			say("no tasks found, stopping.\n");
			return;
		}
		time_started_recipe = System.currentTimeMillis();
		while (true) {
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			if (!is_running)
				break;
			if ((recipe.find_active_tasks().size() == 0)
					&& (recipe.find_available_tasks().size() == 0)) {
				is_running = false;
				time_marker = 0;
				say("execution finished!\n");
				continue;
			}
			if (time_marker == 0)
				time_marker = System.currentTimeMillis();
			long dt = System.currentTimeMillis() - time_marker;
			if (dt > 5000) {
				time_marker = time_marker + dt;
				//say("...\n");
			}
			if ((last_announcment_time > 0)
					&& ((System.currentTimeMillis() - last_announcment_time) < promotion_delay))
				continue;
			LinkedList<Task> ll = recipe.find_available_tasks();
			if (ll.size() > 0) {
				ll.getFirst().start();
				say(ll.getFirst().speech + "\n");
				last_announcment_time = System.currentTimeMillis();
				last_queried_task = ll.getFirst();
				continue;
			}
			ll = recipe.find_overdue_tasks();
			for (Task t : ll) {
				if ((t.time_last_queried != 0)
						&& ((System.currentTimeMillis() - t.time_last_queried) <= query_delay))
					continue;
				t.query_now();
				if ((t.query == "") && (t != last_queried_task)) {
					t.stop();
					set_last_queried_task();
					continue;
				}
				say(t.query + "\n");
				last_announcment_time = System.currentTimeMillis();
				last_queried_task = t;
				break;
			}
		}
	}

	public void done_func() {
		if ((last_queried_task == null) || (!last_queried_task.is_active()))
			return;
		last_queried_task.stop();
		last_announcment_time = last_announcment_time - promotion_delay;
		set_last_queried_task();
		eta_dict = calculate_eta_dict(time_marker);
		//say("done!\n");
	}

	public void repeat_func() {
		set_last_queried_task();
		if (last_queried_task == null) {
			say("currently there is noactive task\n");
			return;
		}
		say(last_queried_task.speech + "\n");
	}
}
